package
{
	import flash.display.*;
	import flash.errors.IllegalOperationError;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.sampler.NewObjectSample;
	import flash.ui.Mouse;
	import flash.utils.getQualifiedClassName;
	import flash.utils.getTimer;
	
	/** This tool is used to outline a closed polygon for the game */
	public class PolygonSelectionTool extends CursorTool
	{
		// STATIC PUBLIC CONSTS
		public static const CHANGE_POLYGON_ON_DELAY:uint = 3000;
		// private objects
		private var currentClosedPolygon:ClosedPolygon = null;
		private var closedPolygons:Array, overClosedPolygons:Array;
		private var prevPoint:Point;
		private var cursor:Sprite = null;
		// private vars
		private var indexOverClosedPolygons:uint;
		private var highestClosedPolygonIndex:uint=0;
		private var timestamp:uint = 0;
		private var messageGiven:Boolean = false;
		
		override public function get components():Array{return new Array(currentClosedPolygon);}
		override public function set halt(b:Boolean):void
		{
			if (b)
			{
				this.alpha = 0;	
				Mouse.show();
			} else
			{
				this.alpha = 1;
				Mouse.hide();
			}
			_halt = b;
		}
		public function PolygonSelectionTool(closedPolygons:Array)
		{
			super(this);
			this.mouseEnabled = false;
			this.closedPolygons = closedPolygons;
			overClosedPolygons = new Array();
			prevPoint = new Point(int.MAX_VALUE, int.MAX_VALUE);
			this.addEventListener(Event.ADDED, handleAdded);
		}
		private function handleAdded(event:Event):void
		{
			this.removeEventListener(Event.ADDED, handleAdded);
			for each (var cp:ClosedPolygon in closedPolygons)
			{
				if (parent.getChildIndex(cp) >  highestClosedPolygonIndex) highestClosedPolygonIndex = parent.getChildIndex(cp);
			}
			drawCursor();
			this.stage.addEventListener(MouseEvent.RIGHT_CLICK, handleRightClick);
			this.addEventListener(Event.REMOVED, handleRemoved);			
		}
		
		/** Will select the closed polygon, usually will be the currently highlighted one, if not updates that reference */
		override public function selectComponent(c:MovieClip):void
		{
			if (!halted)
			{
				var poly:ClosedPolygon = ClosedPolygon(c);
				//this.dispatchEvent(new Event(Event.COMPLETE));
				if (!poly.used)
				{
					poly.outline = false;
					poly.highlight = true;
					currentClosedPolygon = poly; // should be true anyways
					GeometryGameItem(parent.parent).processCommand("polygonSelection", "in_progress", this.parent, new Array(poly));
				} else
				{
					GeometryGameItem(parent.parent).newMessage(parent, "Try another polygon", this.x, this.y, MessageBox.EPHEMERAL_TYPE);
				}
			}
		}
		override public function clearComponent(c:MovieClip):void
		{
			if (!halted)
			{
				var poly:ClosedPolygon = ClosedPolygon(c);
			
				if (poly == currentClosedPolygon)
				{
					poly.highlight = false;
					currentClosedPolygon = null;
				}
			}
		}
		/** Check to see if we are currently over a closed polygon, if so highlight it */
		override public function process(point:Point):void
		{
			if (!halted)
			{
				// have we moved since previous point?
				if (point.x == prevPoint.x && point.y == prevPoint.y)
				{
					// we just stopped moving
					if (timestamp == 0)
					{
						timestamp = flash.utils.getTimer();
					} else
					{
						if (currentClosedPolygon != null)
						{
							// if we have been waiting sufficient time switch the currently highlighted polygon
							if (flash.utils.getTimer() - timestamp > CHANGE_POLYGON_ON_DELAY)
							{
								//trace("shift polygons, prev index", indexOverClosedPolygons, (indexOverClosedPolygons+1)%overClosedPolygons.length);
								timestamp = 0;
								if (!messageGiven && overClosedPolygons.length > 1 && !currentClosedPolygon.highlighted)
								{
									messageGiven = true;
									GeometryGameItem(parent.parent).newMessage(parent, "Right click to find another shape under this point", this.x, this.y, MessageBox.EPHEMERAL_TYPE);
								}
							}
						}
					}
				} else
				{ // moved
					timestamp = 0;
					var newClosedPolygon:Boolean = false;
					if (currentClosedPolygon != null && !currentClosedPolygon.pointWithinBounds(parent.localToGlobal(point)))
					{
						this.currentClosedPolygon.removeEventListener(MouseEvent.CLICK, handleClickOnPolygon);
						currentClosedPolygon.outline = false;	
						currentClosedPolygon = null;
						newClosedPolygon = true;
					} else if (currentClosedPolygon == null)
					{
						newClosedPolygon = true;
					}
					overClosedPolygons = new Array();
					// search for polygons we are currently over
					for each (var poly:ClosedPolygon in closedPolygons)
					{
						if (poly.pointWithinBounds(parent.localToGlobal(point)))
						{
							overClosedPolygons.push(poly);	
						}
						
					}
					if (overClosedPolygons.length > 0 && newClosedPolygon)
					{
						indexOverClosedPolygons = 0;
						this.currentClosedPolygon = overClosedPolygons[indexOverClosedPolygons];
						if (parent.getChildIndex(currentClosedPolygon) < highestClosedPolygonIndex) parent.swapChildrenAt(parent.getChildIndex(currentClosedPolygon), highestClosedPolygonIndex);
						this.currentClosedPolygon.outline = true;
						this.currentClosedPolygon.addEventListener(MouseEvent.CLICK, handleClickOnPolygon);							
					}
				}
				prevPoint = point;
				cursor.x = point.x;
				cursor.y = point.y;
			}
		}
		
		////////////////////////// PRIVATE FUNCTIONS ///////////////////////
		/** Keep the selection mechanism internal */
		private function handleClickOnPolygon (event:MouseEvent):void
		{
			if (flash.utils.getQualifiedClassName(event.currentTarget) == "ClosedPolygon")
			{
				this.currentClosedPolygon.removeEventListener(MouseEvent.CLICK, handleClickOnPolygon);
				this.selectComponent(currentClosedPolygon);
			}
		}
		
		private function handleRightClick (event:MouseEvent):void
		{
			Mouse.hide();
			shiftOverPolygons();
		}
		
		/** Shift from the current polygon under the mouse to another */ 
		private function shiftOverPolygons():void
		{
			if (currentClosedPolygon != null && overClosedPolygons.length > 1)
			{
				this.currentClosedPolygon.removeEventListener(MouseEvent.CLICK, handleClickOnPolygon);
				this.currentClosedPolygon.outline = false;
				var prevClosedPolgyon:ClosedPolygon = currentClosedPolygon;
				indexOverClosedPolygons = (indexOverClosedPolygons+1)%overClosedPolygons.length;
				this.currentClosedPolygon = overClosedPolygons[indexOverClosedPolygons];
				if (parent.getChildIndex(currentClosedPolygon) < highestClosedPolygonIndex) parent.swapChildrenAt(parent.getChildIndex(currentClosedPolygon), highestClosedPolygonIndex);
				this.currentClosedPolygon.outline = true;
				this.currentClosedPolygon.addEventListener(MouseEvent.CLICK, handleClickOnPolygon);
			}
		}
		
		/** Draws a hand cursor to indicate the polygon being selected */
		private function drawCursor():void
		{
			if (cursor == null)
			{
				cursor = new Sprite();
				cursor.mouseEnabled = false;
				this.addChild(cursor);
			} else
			{
				cursor.graphics.clear();
			}
			var p:Point;
			var ang:Number;
			var al:Number = 20, tw:Number = 4, tl:Number = 10;
			ang = MathUtilities.degToRad(160);
			var p1:Point = new Point (al*Math.sin(ang), -al*Math.cos(ang));
			ang = MathUtilities.degToRad(110);
			var p6:Point = new Point(al*Math.sin(ang), -al*Math.cos(ang));
			var aw:Number = MathUtilities.distanceP(p1, p6);
			cursor.graphics.lineStyle(1, 0x444488);
			cursor.graphics.beginFill(0x8888AA, .5);
			p = p1
			cursor.graphics.lineTo(p.x,p.y);
			// to handle
			ang = MathUtilities.degToRad(45);
			p = new Point(p.x+(aw-tw)/2*Math.sin(ang), p.y-(aw-tw)/2*Math.cos(ang));
			cursor.graphics.lineTo(p.x,p.y);
			// out end of handle
			ang = MathUtilities.degToRad(135);
			p = new Point(p.x+tl*Math.sin(ang), p.y-tl*Math.cos(ang));
			cursor.graphics.lineTo(p.x,p.y);
			// other side
			ang = MathUtilities.degToRad(45);
			p = new Point(p.x+tw*Math.sin(ang), p.y-tw*Math.cos(ang));
			cursor.graphics.lineTo(p.x,p.y);
			// back to middle of arrow
			ang = MathUtilities.degToRad(-135);
			p = new Point(p6.x+(aw-tw)/2*Math.sin(ang), p6.y-(aw-tw)/2*Math.cos(ang));
			cursor.graphics.lineTo(p.x,p.y);
			// back to end of arrow
			p = p6;
			cursor.graphics.lineTo(p.x, p.y);
			cursor.graphics.lineTo(0,0);
			cursor.graphics.endFill();				
		}
		
		/** On removal unproject and unoutline */
		private function handleRemoved(event:Event):void
		{
			this.removeEventListener(Event.REMOVED, handleRemoved);
			this.stage.removeEventListener(MouseEvent.RIGHT_CLICK, handleRightClick);
			if (currentClosedPolygon != null)
			{
				if (currentClosedPolygon.hasEventListener(MouseEvent.CLICK)) this.currentClosedPolygon.removeEventListener(MouseEvent.CLICK, handleClickOnPolygon);
			}
		}
	}
}